#include "cmediasdkz.h"
#include "Common/MediaSource.h"
#include "Common/config.h"
#include "Http/HttpRequester.h"
#include "Http/HttpSession.h"
#include "Network/TcpServer.h"
#include "Network/UdpServer.h"
#include "Player/PlayerProxy.h"
#include "Pusher/PusherProxy.h"
#include "Record/MP4Reader.h"
#include "Rtp/RtpProcess.h"
#include "Rtp/RtpServer.h"
#include "Thread/WorkThreadPool.h"
#include "Util/File.h"
#include "Util/MD5.h"
#include "Util/NoticeCenter.h"
#include "Util/logger.h"
#include "Util/onceToken.h"
#include "Util/util.h"
#include <exception>
#include <functional>
#include <math.h>
#include <regex>
#include <signal.h>
#include <sys/stat.h>
#include <unordered_map>

using namespace std;
using namespace toolkit;
using namespace mediakit;

template <typename Type>
class ServiceController {
public:
    using Pointer = std::shared_ptr<Type>;
    std::unordered_map<std::string, Pointer> _map;
    mutable std::recursive_mutex _mtx;

    void clear() {
        decltype(_map) copy;
        {
            std::lock_guard<std::recursive_mutex> lck(_mtx);
            copy.swap(_map);
        }
    }

    size_t erase(const std::string &key) {
        std::lock_guard<std::recursive_mutex> lck(_mtx);
        return _map.erase(key);
    }

    size_t size() {
        std::lock_guard<std::recursive_mutex> lck(_mtx);
        return _map.size();
    }

    Pointer find(const std::string &key) const {
        std::lock_guard<std::recursive_mutex> lck(_mtx);
        auto it = _map.find(key);
        if (it == _map.end()) {
            return nullptr;
        }
        return it->second;
    }

    void for_each(const std::function<void(const std::string &, const Pointer &)> &cb) {
        std::lock_guard<std::recursive_mutex> lck(_mtx);
        auto it = _map.begin();
        while (it != _map.end()) {
            cb(it->first, it->second);
            it++;
        }
    }

    template <class... _Args>
    Pointer make(const std::string &key, _Args &&...__args) {
        // assert(!find(key));

        auto server = std::make_shared<Type>(std::forward<_Args>(__args)...);
        std::lock_guard<std::recursive_mutex> lck(_mtx);
        auto it = _map.emplace(key, server);
        assert(it.second);
        return server;
    }

    template <class... _Args>
    Pointer makeWithAction(const std::string &key, function<void(Pointer)> action, _Args &&...__args) {
        // assert(!find(key));

        auto server = std::make_shared<Type>(std::forward<_Args>(__args)...);
        action(server);
        std::lock_guard<std::recursive_mutex> lck(_mtx);
        auto it = _map.emplace(key, server);
        assert(it.second);
        return server;
    }
};

// 拉流代理器列表
static ServiceController<PlayerProxy> s_player_proxy;

cmediasdkz::cmediasdkz() {}

cmediasdkz::~cmediasdkz() {}

void cmediasdkz::addStreamProxyEx(const string &app, const string &stream, const string &url) {
    auto tuple = MediaTuple { "vhost", app, stream, "" };
    auto key = tuple.shortUrl();
    if (s_player_proxy.find(key)) {
        // 已经在拉流了
        return;
    }
    ProtocolOption option;
    option.enable_hls = false;
    option.enable_mp4 = false;
    // 添加拉流代理
    auto player = s_player_proxy.make(key, tuple, option, 10);

    // 先透传拷贝参数
    // for (auto &pr : args) {
    //     (*player)[pr.first] = pr.second;
    // }

    // 指定RTP over TCP(播放rtsp时有效)
    (*player)[Client::kRtpType] = 0;

    // 播放握手超时时间
    (*player)[Client::kTimeoutMS] = 10 * 1000;

    // 开始播放，如果播放失败或者播放中止，将会自动重试若干次，默认一直重试
    player->setPlayCallbackOnce([key](const SockException &ex) {
        if (ex) {
            s_player_proxy.erase(key);
        }
    });

    // 被主动关闭拉流
    player->setOnClose([key](const SockException &ex) { s_player_proxy.erase(key); });
    player->play(url);
}

bool cmediasdkz::InitLib() {
    return false;
}

void cmediasdkz::addStreamProxy(const string &app, const string &stream, const string &url) {
    EventPollerPool::Instance().getPoller(false)->async([=]() mutable { addStreamProxyEx(app, stream, url); });
}

void cmediasdkz::startSendRtp(
    const string &app, const string &stream, const string &ssrc, const string &dst_url, const string &dst_port, const string &src_port, const string &is_udp) {}

void cmediasdkz::stopSendRtp(const string &app, const string &stream) {}
